package edu.spbsu.nonsm00th.computgeom.util;

import edu.spbsu.nonsm00th.model.Point;
import edu.spbsu.nonsm00th.model.enums.Orientation;
import edu.spbsu.nonsm00th.util.Precondition;
import java.util.ArrayList;
import java.util.List;

/**
 * 2-dimension helper class.
 * 
 * TODO: is it really need to use Precondition - make decision
 * @author mbuzdov
 */
public class D2Util {

    public static Point findInnerPoint(final List<Point> list) {
        Precondition.argsNotNull(list);
        Precondition.check(0 != list.size(), "List of points must contains at least 1 element");

        Point first = list.get(0);
        Point second = null;
        Point third = null;
        int i = 1;
        while (i < list.size()) {
            if ( !first.equals(list.get(i)) ) {
                second = list.get(i);
                i++;
                break;
            }
            i++;
        }
        while (i < list.size()) {
            if ( isPlanarSimplex(list.get(i), first, second) ) {
                third = list.get(i);
                break;
            }
            i++;
        }

        List<Point> container = new ArrayList<Point>();
        container.add(first);
        if (null != second) {
            container.add(second);
        }
        if (null != third) {
            container.add(third);
        }
        return getGeomtricCenter(container.toArray(new Point[0]));
    }

    /**
     * Returns true, if points create planar simplex.
     */
    public static boolean isPlanarSimplex(final Point a, final Point b, final Point c) {
        /**
         * | x1  y1  1 |
         * | x2  y2  1 | != 0
         * | x3  y3  1 |
         */
        return 0 !=
                (b.getX() * c.getY() - c.getX() * b.getY()) -
                (a.getX() * c.getY() - c.getX() * a.getY()) +
                (a.getX() * b.getY() - b.getX() * a.getY());
    }

    /**
     * Returns geometric center of list of points.
     *
     * TODO: move to DNUtil
     * 
     * @param obj the array of points
     * @return the geometric center
     */
    public static Point getGeomtricCenter(final Point ... obj) {
        int size = obj.length;
        Precondition.check(0 != size, "Need at least 1 point in the set");

        double x = 0;
        double y = 0;
        for (int i = 0; i < size; i++) {
            x += obj[i].getX() / size;
            y += obj[i].getY() / size;
        }
        return new Point(x, y);
    }

    public static Orientation getOrientation(final Point a, final Point b, final Point c) {
        Precondition.argsNotNull(a, b, c);
        double S =
                (b.getX() * c.getY() - c.getX() * b.getY()) -
                (a.getX() * c.getY() - c.getX() * a.getY()) +
                (a.getX() * b.getY() - b.getX() * a.getY());

        if (0 < S) {
            return Orientation.ABOVE;
        }
        if (0 == S) {
            return isOrdered(a, b, c) ? Orientation.UNDER : Orientation.ABOVE;
        }
        return Orientation.UNDER;
    }

    private static boolean isOrdered(final Point a, final Point b, final Point c) {//must located on same line
        return ((a.getX() <= b.getX() && b.getX() <= c.getX()) || (a.getX() >= b.getX() && b.getX() >= c.getX())) &&
                ((a.getY() <= b.getY() && b.getY() <= c.getY()) || (a.getY() >= b.getY() && b.getY() >= c.getY()));
    }

}
